﻿#region File and License Information
/*
<File>
	<License>
		Copyright © 2009 - 2017, Daniel Vaughan. All rights reserved.
		This file is part of Calcium (http://calciumsdk.net), 
		which is released under the MIT License.
		See file /Documentation/License.txt for details.
	</License>
	<CreationDate>2011-12-15 16:46:11Z</CreationDate>
</File>
*/
#endregion

using System;
using System.ComponentModel;
using System.Reflection;

using Outcoder.ComponentModel;

namespace Outcoder.CommandModel
{
	public abstract class CommandBase<T> : IEventCommand, INotifyPropertyChanged
	{
#if WINDOWS_PHONE
		string eventName = "Tap";
#else
		string eventName = "Click";
#endif

		public string EventName
		{
			get
			{
				return eventName;
			}
			set
			{
				eventName = value;
			}
		}

		bool previousCanExecute;

		protected CommandBase()
		{
		}

		#region ICommand Members

#if SILVERLIGHT || MONODROID || __IOS__ || NETFX_CORE
		public event EventHandler CanExecuteChanged;
#else
		public event EventHandler CanExecuteChanged
		{
			add
			{
				System.Windows.Input.CommandManager.RequerySuggested += value;
			}
			remove
			{
				System.Windows.Input.CommandManager.RequerySuggested -= value;
			}
		}
#endif

		public bool CanExecute(object parameter)
		{
			object coercedParameter = CoerceParameterToType(parameter);
			return CanExecute((T)coercedParameter);
		}

		public void Execute(object parameter)
		{
			object coercedParameter = CoerceParameterToType(parameter);
			ExecuteInternal((T)coercedParameter);
		}

		object CoerceParameterToType(object parameter)
		{
			object coercedParameter = parameter;
			Type typeOfT = typeof(T);
#if NETFX_CORE
			if (parameter != null && !typeOfT.GetTypeInfo().IsAssignableFrom(parameter.GetType().GetTypeInfo()))
#else
			if (parameter != null && !typeOfT.IsAssignableFrom(parameter.GetType()))
#endif
			{
				coercedParameter = ImplicitTypeConverter.ConvertToType(parameter, typeOfT);
			}
			return coercedParameter;
		}

		#endregion

		public bool CanExecute(T parameter)
		{
			bool temp = CanExecuteInternal(parameter);

			if (previousCanExecute != temp)
			{
				previousCanExecute = temp;
#if SILVERLIGHT
				OnCanExecuteChanged(EventArgs.Empty);
#endif
			}

			return previousCanExecute;
		}

		protected abstract bool CanExecuteInternal(T parameter);

		protected abstract void ExecuteInternal(T parameter);

#if SILVERLIGHT || MONODROID || __IOS__ || NETFX_CORE
		public void RaiseCanExecuteChanged()
		{
			OnCanExecuteChanged(EventArgs.Empty);
		}

		protected virtual void OnCanExecuteChanged(EventArgs e)
		{
			var tempHandler = CanExecuteChanged;
			if (tempHandler != null)
			{
				UISynchronizationContext.Instance.InvokeIfRequired(() => tempHandler(this, e));
			}
		}
#else
		public void RaiseCanExecuteChanged()
		{
			OnCanExecuteChanged(EventArgs.Empty);
		}

		protected virtual void OnCanExecuteChanged(EventArgs e)
		{
			/* This method is present for cross-platform compatibility. */
		}
#endif

		/// <summary>
		/// The title of this command that may be displayed to the user.
		/// </summary>
		public string Text
		{
			get
			{
				return textFunc != null ? textFunc() : null;
			}
		}

		Func<string> textFunc;

		/// <summary>
		/// A func to retrieve the text of the command.
		/// </summary>
		public Func<string> TextFunc
		{
			set
			{
				if (textFunc != value)
				{
					textFunc = value;
					OnPropertyChanged("TextFunc");
					OnPropertyChanged("Text");
				}
			}
		}

		public event PropertyChangedEventHandler PropertyChanged;

		protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
		{
			var tempHandler = PropertyChanged;
			if (tempHandler != null)
			{
				UISynchronizationContext.Instance.InvokeIfRequired(() => tempHandler(this, e));
			}
		}

		protected virtual void OnPropertyChanged(string propertyName)
		{
			OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
		}
	}
}
